/*
 * Group: Jan Stalhut, Dennis Neumann, Willi Pfeifer , Mirco Timmermann
 */

package de.hsb.ai.agent.misterx;

import game.MapLabel;
import game.Move;
import jade.core.AID;

import java.util.List;

import de.hsb.ai.agent.AbstractFugitive;
import de.hsb.ai.agent.misterx.brain.Brain;
import de.hsb.ai.agent.misterx.minimax.AlphaBeta;
import de.hsb.ai.agent.misterx.minimax.ISearchStrategy;
import de.hsb.ai.agent.misterx.minimax.ITerminalTest;
import de.hsb.ai.agent.misterx.minimax.TerminalTest;
import de.hsb.ai.agent.misterx.pathheuristic.INodeHeuristic;
import de.hsb.ai.agent.misterx.targetmove.ITargetMove;
import de.hsb.ai.agent.misterx.pathfinding.Node;
import de.hsb.ai.agent.misterx.pathfinding.Pathfinding;
import de.hsb.ai.agent.misterx.pathheuristic.DangerzoneHeuristic;
import de.hsb.ai.agent.misterx.pathheuristic.DummyHeuristic;
import de.hsb.ai.agent.misterx.pathheuristic.WeightHeuristic;
import de.hsb.ai.agent.misterx.targetmove.DummyMove;
import de.hsb.ai.agent.misterx.targetmove.TargetMoveAvoid;
import de.hsb.ai.domain.GameOver;
import de.hsb.ai.domain.GameStarted;
import de.hsb.ai.domain.MoveEvent;


@SuppressWarnings("serial")
public class MisterXAgent extends AbstractFugitive {

	private Brain _brain;
	
	//Minimax
	private ISearchStrategy _searchStrategy;
	private ITerminalTest _terminalTest;
	
	//Pathfinding
	private Pathfinding _pathfinding;
	private INodeHeuristic _nodeHeuristic;
	private ITargetMove _targetMove;
	
	private List<Node> _walkingPath;
	private int _pathPointer = 1;

	MisterXAgentGUI _misterXAgentGUI;
	
	private Drawer _drawer = null;
	private boolean _drawActivate = true;
	
	
	public MisterXAgent() {
		super("MisterX");
	}
	
	/////////////////////////////////////////////////////////////////////////////
	//  GAME
	/////////////////////////////////////////////////////////////////////////////
	
	@Override
	protected void setup() {
		super.setup();
		this._brain = new Brain(this.getAID(), map);
		this._drawer = new Drawer(Brain.GEOANALYSER);
		MapLabel._draw = this._drawer;
		this._drawer.activate(this._drawActivate);
	}

	/**
	 * wird zu Beginn des Starts einmal ausgefürht (zur initialisierung)
	 */
	@Override
	protected void handleGameStarted(GameStarted g) {
		super.handleGameStarted(g);
		this._brain.handleGameStarted(g);
		
		this._pathfinding = new Pathfinding();

		this._misterXAgentGUI = new MisterXAgentGUI();
	}
	
	/**
	 * wird einmal ausgeführt wenn Spiel zu Ende ist (für aufräumarbeiten)
	 */
	@Override
	protected void handleGameOver(GameOver g) {
		super.handleGameOver(g);
		this._brain.handleGameOver(g);
	}
	
	/**
	 * für jeden Spielerzug ausgeführt.
	 */
	@Override
	protected void handleMove(MoveEvent m) {
		super.handleMove(m);
		this._brain.handleMove(m);
	}

	/**
	 * wird jedes mal wenn wir am Zug sind ausgeführt (Entscheidung des Zuges)
	 */
	@Override
	protected Move getMove(List<Move> options) {
		this._brain.getMove(options);
		
		int optionIndex = 0;
		optionIndex = this.chooseStrategie(options);
		
		return options.get(optionIndex);
	}
	
//------------------------------------------------------------------------------------	
// STRATEGIES
//------------------------------------------------------------------------------------	
	
	/**
	 * sucht automatisch die beste Strategy aus.
	 */
	private int chooseStrategie(List<Move> options) {
		int a = 1;
		
		boolean calculateNewPath = this.stabilisationPath(1);
		if(Brain.getCurrentRound() == 0)  calculateNewPath = true;
		
		if(calculateNewPath) {
			if(a == 0) { //ONLY PATHFINDING
				this.setTargetMove(new DummyMove(190));
				this.setPathHeuristic(new DummyHeuristic());
				return this.getBestOption_Pathfinding(options, true);
			}
			
			if(a == 1) {
				String currentTechnic = "";

				int bestOption = 0;
				
				if(Brain.isMrXBetweenEnemies() && Brain.isEnemyAreaBig() == false) {
					currentTechnic = "MINIMAX";
					this.setTerminalTest(new TerminalTest());
					bestOption =  getBestOption_MinimaxTest(options);
					Drawer.addPath(null);
				} else {
					currentTechnic = "PATHFINDING";
					this.setTargetMove(new TargetMoveAvoid());
					this.setPathHeuristic(new WeightHeuristic());
					bestOption =  this.getBestOption_Pathfinding(options, true);
					
					//Verhindert das der Pathfinding trotz den Heuristiken falsch agiert.
						if(Brain.GRAPHANALYSER.isNodeSave(options.get(bestOption).getNode()) == false) { //!AlphaBetaHelper.isSaveNode(options.get(bestOption).getNode())){
							currentTechnic = "MINIMAX";
							this.setTerminalTest(new TerminalTest());
							bestOption = getBestOption_MinimaxTest(options);
							Drawer.addPath(null);
						} else {
							Drawer.addPath(this._walkingPath);
						}
					//
						
				}
				
				Drawer.addMessage(currentTechnic);
				return bestOption;
			}
			
		}
		
		return getBestOption_Pathfinding(options, false);
	}

	
	int _counter = 0;
	private boolean stabilisationPath(int round) {	
		boolean calculateNewPath = true;
		if(this._counter % round == 0) {
			System.out.println(Brain.getCurrentRound() + ":" + "newPATH");
			calculateNewPath = true;
			this._pathPointer = 1;
		} else {
			calculateNewPath = false;
			this._pathPointer++;
		}
		this._counter++;
		return calculateNewPath;
	}
	
//------------------------------------------------------------------------------------	
// PATHFINDING
//------------------------------------------------------------------------------------		

	/**
	 * ändert die Heuristik-klasse des Pathfindings ab. (Strategy)
	 */
	public void setPathHeuristic(INodeHeuristic nodeHeuristic) {
		this._nodeHeuristic = nodeHeuristic;
	}
	
	/**
	 * ändert den angepeilten Endnode des Pathfindings ab. Strategy)
	 * @param nodeIndex
	 */
	private void setTargetMove(ITargetMove targetMove) {
		this._targetMove = targetMove;
		int typ = this._targetMove.setup(Brain.getEnemyCount());
		if(Brain.isEnemyOne() && typ == ITargetMove.DOES_ONE
				|| Brain.isEnemyTwo() && typ == ITargetMove.DOES_TWO
				|| Brain.isEnemyMulti() && typ == ITargetMove.DOES_MULTI
				|| typ == ITargetMove.DOES_ALL) 
		{	
			//System.out.println("ERROR: TARGETMOVE NOT COMPATIBLE");
		} 
		
	}
	
	/**
	 * PATHFINDING getBestOption
	 * @param options
	 * @return
	 */
	private int getBestOption_Pathfinding(List<Move> options, boolean a) {
		int optionIndex = 0;

		if(a) {
			this.calculatePath();
		}
		
		//nimmt den vorgegebenen Path.
		if(this._walkingPath != null) {
			if(this._pathPointer < this._walkingPath.size()) {
				if(this._pathPointer < this._walkingPath.size()) {
					Node toWalkNode = this._walkingPath.get(this._pathPointer);
					
					for(int i = 0; i < options.size(); i++) {
						if(options.get(i).getNode() == toWalkNode._index) {
							optionIndex = i;
							break;
						}	
					}
				}
			}
		}
		
		
		return optionIndex;
	}
	
	/**
	 * Berechnet den Weg mittels Pathfinding.
	 */
	private void calculatePath() {
		Move move = Brain.HISTORYCAPTURER.getLastPlayerMove(this.getAID());
		if(move != null) {
			this._walkingPath = this._pathfinding.getPath(move.getNode(), this._targetMove.getNodeIndex(), this._nodeHeuristic);
		}
	}
	
//------------------------------------------------------------------------------------	
//MINMAX
//------------------------------------------------------------------------------------	
	
	/**
	 * setzt die Terminal-Nodes des Minimax ab. (Strategy)
	 */
	private void setTerminalTest(TerminalTest terminalTest) {
		this._terminalTest = terminalTest;
	}
	
	/**
	 * MINIMAX getBestOption
	 * @param options
	 * @return
	 */
	private int getBestOption_MinimaxTest(List<Move> options) {
		this._searchStrategy = new AlphaBeta(this._terminalTest);
		int optionIndex = 0;
		
		Move move = Brain.HISTORYCAPTURER.getLastPlayerMove(this.getAID());
		if(move != null) {
			de.hsb.ai.agent.misterx.minimax.Node node = new de.hsb.ai.agent.misterx.minimax.Node();
			node._mrXState = move.getNode();
			List<AID> detectives = Brain.getEnemyAIDs();
			for(AID aid : detectives) {
				//letzte Position des Detectives ermitteln
				move = Brain.HISTORYCAPTURER.getLastPlayerMove(aid);
				int pos = move.getNode();
				node._detectiveStates.put(aid, pos);
			}
			
			int nextNode = this._searchStrategy.SEARCH(node);
			
			for(int i=0; i<options.size(); i++){
				if(options.get(i).getNode() == nextNode){
					return i;
				}
			}
		}

		
		return optionIndex;
	}
	
	
	
}	
